package com.hs.user.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.hs.common.api.dto.ResultCode;
import com.hs.common.api.dto.ResultVo;
import com.hs.pk.berth.client.BerthFeignClient;
import com.hs.pk.berth.client.dto.RoadBerthDto;
import com.hs.user.constant.CarConstant;
import com.hs.user.constant.OrderConstant;
import com.hs.user.constant.RedisConstant;
import com.hs.user.constant.UserConstant;
import com.hs.feign.client.OrderClient;
import com.hs.jwt.JwtTemplate;
import com.hs.user.bean.User;
import com.hs.user.dto.BerthDto;
import com.hs.user.dto.ParkingDto;
import com.hs.user.exception.Asserts;
import com.hs.user.mapper.UserMapper;
import com.hs.user.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zxd.feign.client.CarFeignClients;
import com.zxd.feign.dto.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * <p>
 * 用户 服务实现类
 * </p>
 *
 * @author Lin GongZi
 * @since 2022-07-20
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderClient orderClient;

    @Autowired
    private JwtTemplate jwtTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private BerthFeignClient berthFeignClient;

    @Autowired
    private CarFeignClients carFeignClients;

    /**
     * 登陆服务
     * @param
     * @return
     * 登陆服务
     *  1.通过手机号查询数据库，判断返回结果是否为null
     *  	1.1 null：自动保存客户信息到数据库
     *  	1.2 not null：返回登陆状态
     *  输入：手机号
     *  输出：登陆状态
     */
    @Override
    public ResultVo login(User user) {
        //手机号校验
        Pattern pattern = Pattern.compile("^1[3|4|5|7|8][0-9]{9}$");
        if (!pattern.matcher(user.getTelephone()).matches()){
            Asserts.failed("请填写正确的手机号码");
        }

        //判断是否为新用户
        User resultUser = userMapper.selectById(user.getTelephone());
        if (ObjectUtils.isEmpty(resultUser)){
            user.setCreateTime(new Date());
            user.setStatus("可用");
            int i = userMapper.insert(user);
        }

        //生成token
        Map<String,Object> payload = new HashMap<>();
        payload.put(UserConstant.USER_TELEPHONE,user.getTelephone());
        String token = jwtTemplate.createToken(payload);

        //将token存储到redis中，设置有效时间30分钟
        redisTemplate.opsForValue().set(RedisConstant.USER_TOKEN+user.getTelephone(),token,30, TimeUnit.MINUTES);

        //获取所有未支付订单
        ResultVo resultVo = orderClient.findUnPayOrders(user.getTelephone());
        if (!ObjectUtils.isEmpty(resultVo.getData())){
            return new ResultVo(ResultCode.UNPAY,token);
        }
        return new ResultVo(ResultCode.LOGIN,token);
    }

    /**
     * 泊车服务
     * @param berthId
     * @param request
     * @return
     *泊车服务：
     * 扫码停车或输入泊车号，对泊车号进行判断
     * 1.无效泊车号，请重试 （null）
     * 2.泊位已被占用 （判断状态是否被占用）
     * 3.无地磁车辆，重新驶入检测 （查询redis中泊位号是否存在订单中，如果为null提示车辆未驶入）
     * 4.无异常，正常认领，返回相关数据进行二次确认，开始计时
     * 	4.1 将信息储存进redis
     * 	输入：泊车号
     * 	输出：parkingd     */
    @Override
    public ParkingDto parking(Long berthId, HttpServletRequest request) throws ParseException {
        //判断泊位状态是否被占用
        RoadBerthDto parkingDtoByBerthId = berthFeignClient.findParkingDtoByBerthId(berthId);
        Integer status = parkingDtoByBerthId.getBerthStatus();
        if (status == 1){
            Asserts.failed("泊位号已被占用，请重新输入");
        }
        //查询redis中泊位号是否存在订单中，如果为null提示车辆未驶入
        //电话 状态
//        Object order = redisTemplate.opsForHash().values("路段号：泊位号");
        String redisKey = "tempOrder:" + "roadId:" + parkingDtoByBerthId.getRoadId() + ":berthId:" + berthId;
        Object order = redisTemplate.opsForHash().values(redisKey);
        if (ObjectUtils.isEmpty(order)){
            Asserts.failed("无地磁车辆，请重新驶入检测");
        }
        //将用户电话、订单状态存储进redis
        redisTemplate.opsForHash().put(redisKey,"tel",request.getHeader(UserConstant.USER_TELEPHONE));
        redisTemplate.opsForHash().put(redisKey,"orderStatus", OrderConstant.ORDER_STATUS_CLAIM);

        //组装数据返回
        ParkingDto parkingDto = new ParkingDto();
        parkingDto.setBerthId(berthId);
        parkingDto.setRoadName(parkingDtoByBerthId.getRoadName());

        //从redis中取出驶入时间
        Date driveInTime = (Date) redisTemplate.opsForHash().get(redisKey, "driveInTime");
        //计算停车时长
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String currentTime = sdf.format(new Date());
        String driveInTimeFormat = sdf.format(driveInTime);
        Date parse1 = sdf.parse(currentTime);
        Date parse2 = sdf.parse(driveInTimeFormat);
        long parkingTime = parse1.getTime() - parse2.getTime();
//        parkingDto.setParkingTime(parse1.getTime() - parse2.getTime());
        long diff = parse1.getTime() - parse2.getTime();
        long days = diff / (1000 * 60 * 60 * 24);
        long hours = (diff-days*(1000 * 60 * 60 * 24))/(1000* 60 * 60);
        long minutes = (diff-days*(1000 * 60 * 60 * 24)-hours*(1000* 60 * 60))/(1000* 60);
        System.out.println(""+days+"天"+hours+"小时"+minutes+"分");
        parkingDto.setParkingTime(""+days+"天"+hours+"小时"+minutes+"分");

        parkingDto.setEnterTime(driveInTimeFormat);
        parkingDto.setParkingFee(new BigDecimal(0));

        return parkingDto;
    }

    /**
     * 停车缴费
     * @param orderId
     * @return
     */
    @Override
    public ResultVo payParking(Long orderId) {
        return orderClient.finishOrder(orderId);
    }

    /**
     * 车位搜索
     * @param
     * @return
     */
    @Override
    public List<BerthDto> berthSearch(Double currentLongitude, Double currentLatitude) {
        if (currentLongitude == null || currentLatitude == null){
            Asserts.failed("请打开定位功能");
        }
        Circle circle = new Circle(new Point(currentLongitude, currentLatitude), new Distance(200, Metrics.KILOMETERS));//Point(经度, 纬度) Distance(距离量, 距离单位)
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(5);
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo()
                .radius("geo",circle,args);//params: key, Circle, GeoRadiusCommandArgs

        Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = results.iterator();
        List<BerthDto> list = new ArrayList<>();
        while (iterator.hasNext()){
            BerthDto berthDto1 = new BerthDto();
            GeoResult<RedisGeoCommands.GeoLocation<String>> next = iterator.next();
            String roadName = next.getContent().getName();
            com.hs.pk.berth.dto.BerthDto berthDto = berthFeignClient.searchBerthDtoByRoadName(roadName);
            System.out.println("berthDto = " + berthDto);
            berthDto1.setRoadName(roadName);
            berthDto1.setLocation(berthDto.getLocation());
            berthDto1.setChargeType(berthDto.getFeeType());
            berthDto1.setFreeParkingSpace(berthDto.getBerthNum());
            Distance distance = next.getDistance();
            berthDto1.setDistance(distance.toString());
            list.add(berthDto1);
        }
        return list;
    }

    /**
     * 停车订单显示
     * @param licensePlate
     * @param
     * @return
     */
    @Override
    public ResultVo parkingOrder(String licensePlate) {
        if(StringUtils.hasText(licensePlate)){
            //如果车牌号不为空，只返回未支付订单
            return orderClient.findOrderByConditions(null, licensePlate, null, OrderConstant.ORDER_STATUS_UNPAY);
        }else {
            //如果未搜索车牌号  返回所有订单
            return orderClient.findOrderByConditions(null, licensePlate, null,null);
        }
    }

    /**
     * 车辆绑定
     * @param licensePlate
     * @param plateType
     * @return
     */
    @Override
    public ResultVo vehicleBinding(String licensePlate, String plateType,HttpServletRequest request) {
        if (!StringUtils.hasText(licensePlate) || !StringUtils.hasText(plateType)){
            Asserts.failed("车牌类型或车牌号不可为空");
        }
        if (licensePlate.length() != 7){
            Asserts.failed("请正确输入车牌号");
        }
        Car car = new Car();
        car.setLicensePlate(licensePlate);
        car.setTelephone(request.getHeader(UserConstant.USER_TELEPHONE));
        car.setPlateType(plateType);
        car.setCarStatus(0);
        return carFeignClients.addCar(car);
    }

}
















































